<?php

//@author mran3
//http://code.google.com/p/mran3stuff/

//Cambios respecto a 4.0:
//-Opción para que la cajita de errores no tenga obligatoriamente la
//opción de cerrar.
//-Se le quitaron los estilos hardcoded al div de cerrar cajita
//-Se ordenan los métodos alfabeticamente
//se agregan/modifican algunos comentarios.

// APUNTES PARA EL USUARIO:
// La forma usual de utilizar esta clase consiste en crear un arreglo que
// especificará las reglas que tendrá cada campo, y las opciones para cada regla
// después se llama a la función validar(), con este arreglo como parámetro. Esta
// función devuelve true sólo si todos los campos son válidos, de lo contrario
// devuelve false, y en $this->arErrores['nombre del campo'], se especifica el
// mensaje de error para cada campo.

// APUNTES PARA EL DESARROLLADOR:
// Todas las funciones (reglas) de validación reciben 2 argumentos (muestra y opciones)
// opciones es un array que puede contener parámetros específicos para cada regla, pero 
// en todas se tiene en cuenta el parámetro $opciones['msjError'] que indica que mensaje
// de error q debe devolver la regla en caso de que la muestra no cumpla los requisitos
// de la regla, pej. $opciones['msjError'] = 'Ingrese un email válido'.

//Las reglas tienen una visibilidad 'protected'

require_once 'phpmailer/class.phpmailer.php';

class FormularioAEmail extends PHPMailer {

    /**
      Validate an email address.
      Provide email address (raw input)
      Returns true if the email address has the email
      address format and the domain exists.
      http://www.linuxjournal.com/article/9585
     */
    var $CharSet = 'UTF-8';
    //sobreescribimos 'iso-8859-1';
    var $para;
    var $html = true;
    var $arErrores = array();

    public function __construct() {
    parent::__construct();  
    }
    
    //Imprime (o devuelve) el error de validación (en caso de que exista) que se haya producido
    //al validar el campo $campo si existe, si no hubo error no imprime (o devuelve) nada.
    public function _msgError($campo, $imprimir=true) {
        if (isset($this->arErrores[$campo])) {
            if ($imprimir) {
                echo $this->arErrores[$campo];
            } else {
                return $this->arErrores[$campo];
            }
        }
    }
    
    //Recibe un array y devuelve una cadena con sus elementos
    //separados por comas, p.ej. si se le envia:
    //array(array('heucalipto','roble'),'banano');
    //devolverá: "array('heucalipto', 'roble'), 'banano'"
    //(Es similar a implode() pero sirve con arrays)
    private function arr2csv($arreglo) {
        $cadArgs = '';
        foreach ($arreglo as $valor) {
            if (is_array($valor)) {
                $cadArgs .= 'array(' . arr2csv($valor) . '), ';
            } else {
                $cadArgs .= "'" . $valor . "', ";
            }
        }
        if ($cadArgs != '') {
            $cadArgs = substr($cadArgs, 0, -2);
        }
        return $cadArgs;
    }

    //Esta funcion usando lo que haya en $_POST, llena el arreglo que se le
    //envíe para después usarlo con la función validar
    private function autoIndicesContenido(&$arValidacion) {
        foreach ($_POST as $nombreCampo => $valor) {
            $arValidacion[$nombreCampo]['contenido'] = $valor;
        }
    }
    
    //Genera un mensaje con las variables que haya en $_POST o $_GET, se puede
    //usar un prefijo en los nombres de las variables exluir algunas de ellas.
    //Tener en cuenta al nombrar los campos que se reemplaza "_" por " "
    //$html indica sí se usará HTML para formatear el contenido del mensaje o por
    //el contrario se lo dejará como texto plano
    public function autoMensaje($metodo="post", $html=true, $prefijoOcultas="oculto_") {
        $varsRecibidas = $metodo == "post" ? $_POST : $_GET;

        $cadena = "";

        foreach ($varsRecibidas as $nomVariable => $contenidoVar) {

            $inicioVar = substr($nomVariable, 0, strlen($prefijoOcultas));

            if ($inicioVar != $prefijoOcultas && $contenidoVar != "") {

                if ($nomVariable != ("x") && $nomVariable != ("y")) {
                    //Para omitir coordenadas cuando se usa una imágen en vez de un botón de submit

                    $nomVariable = str_replace("_", " ", $nomVariable);
                    $nomVariable = ucfirst($nomVariable);

                    if ($html) {
                        $cadena .= "<b>$nomVariable</b>: " . $contenidoVar . "<br />";
                    } else {
                        $cadena .= "-$nomVariable: " . $contenidoVar . "\n";
                    }
                }
            }
        }

        return $cadena;
    }
    

    /* Devuelve el html de una cajita flotante con los errores.
     * Si el parámetro $opcionCerrar es false, no muestra la opción para ocultar
     * la cajita.
     * Es necesario algo de css para que se vea bien, por ejemplo definir estilos
     * para #divErrores, #cerrarCajaErrores, etc. (en formulario_a_email.css) hay
     * unos predefinidos.
     */
    public function cajitaErrores($opcionCerrar=true) {

        $html = '<div id="divErrores">';
            if($opcionCerrar) {
                $html .= '<div id="cerrarCajaErrores"> <a href="#" onclick="document.getElementById(\'divErrores\').style.visibility =\'hidden\'"> Cerrar [x]</a></div>';
            }            
            $html .= '<div><strong>Por favor corrija los siguientes errores:</strong></div>';
            $html .= '<ul>';
    
                foreach ($this->arErrores as $campo => $error) {
                    if ($error != "") {
                        $html .= '<li><strong>' . ucfirst($campo) . ': </strong>' . $error . '</li>';
                    }
                }
    
            $html .= '</ul>';
        $html .= '</div>';
        return $html;
    }
    

    //Regla de validación para emails
    //Comenté la parte que verifica que el dominio exista
    protected function email($muestra, $opciones=array()) {
        $email = $muestra; //TODO simplemente cambiar $email por muestra en el resto de la función
        $isValid = true;
        $atIndex = strrpos($email, "@");
        if (is_bool($atIndex) && !$atIndex) {
            $isValid = false;
        } else {
            $domain = substr($email, $atIndex + 1);
            $local = substr($email, 0, $atIndex);
            $localLen = strlen($local);
            $domainLen = strlen($domain);
            if ($localLen < 1 || $localLen > 64) {
                // local part length exceeded
                $isValid = false;
            } else if ($domainLen < 1 || $domainLen > 255) {
                // domain part length exceeded
                $isValid = false;
            } else if ($local[0] == '.' || $local[$localLen - 1] == '.') {
                // local part starts or ends with '.'
                $isValid = false;
            } else if (preg_match('/\\.\\./', $local)) {
                // local part has two consecutive dots
                $isValid = false;
            } else if (!preg_match('/^[A-Za-z0-9\\-\\.]+$/', $domain)) {
                // character not valid in domain part
                $isValid = false;
            } else if (preg_match('/\\.\\./', $domain)) {
                // domain part has two consecutive dots
                $isValid = false;
            } else if
            (!preg_match('/^(\\\\.|[A-Za-z0-9!#%&`_=\\/$\'*+?^{}|~.-])+$/',
                            str_replace("\\\\", "", $local))) {
                // character not valid in local part unless
                // local part is quoted
                if (!preg_match('/^"(\\\\"|[^"])+"$/',
                                str_replace("\\\\", "", $local))) {
                    $isValid = false;
                }
            }
            /*  if ($isValid && !(checkdnsrr($domain,"MX") || checkdnsrr($domain,"A")))
              {
              // domain not found in DNS
              $isValid = false;
              } */
        }
        if (!$isValid) {
            //$msgError="Por Favor ingresar un e-mail valido."
            $msjError = isset($opciones['msjError']) ? $opciones['msjError'] : "ingrese un e-mail valido.";
            return $msjError;
        } else {
            return true;
        }
    }


    
    //Esta es la función que debe llamarse desde el html para enviar el correo
    //una vez que se han especificado las propiedades necesarias
    //destinatario, contenido del msj. etc.
    //Si se han establecido las propiedades necesarias (host, username, password, etc)
    //se hace el envío por SMTP, de lo contrario se utiliza la función mail()
    public function enviarMail() {

       if ($this->Body == ""){
            echo "Mensaje en blanco";
            return false;
        }

        $this->Subject = $this->Subject == "" ? "Envio desde el sitio web" : $this->Subject;

        $this->FromName = $this->FromName == "Root User" ? "Sitio web" : $this->FromName;

        if ($this->html) {
            $this->IsHTML();
        }

        //Detectamos si se enviará por smtp o mail
        if ($this->Username != null && $this->Password != null && $this->Host != null && $this->Port != null) {
            $this->IsSMTP();
            $this->SMTPAuth = true;
            $this->From = $this->Username;
        } else {
            $this->IsMail();
        }

        //agregamos los destinatarios
        if (is_string($this->para)) {
            $destinos = explode(",", $this->para);
        }

        foreach ($destinos as $destino) {
            $this->AddAddress(trim($destino));
        }
        try {
            $this->Send();
            return true;
        } catch (phpmailerException $e) {
            //echo $e->errorMessage();
            
            //Si falla el envío por smtp, se intenta con la función mail()
            if($this->SMTPAuth){
                $this->IsMail();
            }
            return $this->Send();
            
        }
    }



    
    
    //TODO probar con la opcion de excepciones en los carácteres permitidos
    protected function numerico($muestra, $opciones=array()) {

        $msjError = isset($opciones['msjError']) ? $opciones['msjError'] : "Ingrese solo numeros";

        if (isset($opciones['excepciones'])) {

            $excepciones = $opciones['excepciones'];
            if (is_array($excepciones)) {
                foreach ($excepciones as $excepcion) {
                    $muestra = str_replace($excepcion, '', $muestra); //quitamos las excepciones de la cadena
                }
            } else {
                $muestra = str_replace($excepciones, '', $muestra); //quitamos las excepciones de la cadena
            }
        }

        if (is_numeric($muestra)) {
            return true;
        } else {
            return $msjError;
        }
    }
    
    //Regla de validación para campos que tienen que estar presentes
    protected function requerido($muestra=null, $opciones=array()) {
        $res = isset($opciones['msjError']) ? $opciones['msjError'] : "Campo Obligatorio";

        if ($muestra !== null) {
            if ($muestra !== "") {
                $res = true;
            }
        }
        return $res;
    }

    //TODO revisar enviando parámetros con backslashes ¿pueden prodcuir errores en el eval?
    //Recibe un arreglo de la sig. forma
    // $validacion['telefono']['contenido'] = "23353223"
    // $validacion['telefono']['reglas']["numerico"] = ""
    // En los casos que se requieren parámetros adicionales sería algo así:
    // $validacion['telefono']['reglas']["numerico"] = array("excepciones"=>"-")
    // validar () devuelve true en caso de que no hayan errores o un array de la forma:
    // $errores['nombre'] = "Este campo es obligatorio"
    // $errores['telefono'] = "Ingrese sólo números"
    // (Si hay más de un fallo por campo, no importa, se muestra sólo 1 (para evitar que se descuadren los diseños html)
    public function validar($arValidacion) {
        $validez = true;
        $this->arErrores = array(); //Lo limpiamos
        $this->autoIndicesContenido($arValidacion);
        foreach ($arValidacion as $nomCampo => $reglasyContenido) {
            if (!isset($reglasyContenido['reglas'])
                )continue;
            foreach ($reglasyContenido['reglas'] as $regla => $opciones) {
                $reglasyContenido['contenido'] = isset($reglasyContenido['contenido']) ? $reglasyContenido['contenido'] : null;
                if (empty($opciones)) {
                    eval('$res = $this->' . $regla . '("' . $reglasyContenido['contenido'] . '");');
                } else {
                    eval('$res = $this->' . $regla . '("' . $reglasyContenido['contenido'] . "\", \$opciones);");
                }
                if ($res !== true) {
                    $this->arErrores[$nomCampo] = $res;
                    $validez = false;
                    break;
                } else {
                    $this->arErrores[$nomCampo] = ""; //Para q en el html no diga "undefined"
                }
            }
        }
        return $validez;
    }



    /*
    Creo que estas tres funciones venian de versiones anteriores, si no hay conflictos,
    borrar en siguientes versiones ->agregado en versión 4.1
    
    
    //Esta regla de validación devuelve true sólo si todos los campos q se
    //indiquen en el array $campos se encuentran en el arreglo $_POST y no
    //son vacios.
    
    //TODO creo que hay q comprobar que si uno de los campos enviados en $campos
    //no existe en $_POST, puede producirse un error    
    protected function camposObligatorios($campos, $msgError="Todos los campos son obligatorios") {
        foreach ($campos as $campo) {
            if ($_POST[$campo] == "")
                return $msgError;
        }
        return true;
    }    

    //Función para enviar correos html con la clase phpmailer v. 5, SIN usar smtp.
    //$to puede ser un array q contenga varios destinatarios, o una cadena con una sola dir. de correo
    public function correoHtMail($to, $from, $fromName, $subject, $msgHTML) {

        require_once 'phpmailer/class.phpmailer.php';
        try {
            $mail = new PHPMailer(true);

            if (is_array($to)) {
                foreach ($to as $destino) {
                    $mail->AddAddress($destino);
                }
            } else { //si es una cadena (mimail@srv.com)
                $mail->AddAddress($to);
            }

            $mail->IsMail();  //Se le indica que use función mail de php
            $mail->IsHTML(true);
            $mail->CharSet = 'UTF-8';
            $mail->From = $from;
            $mail->FromName = $fromName;
            $mail->Subject = $subject;
            $mail->Body = $msgHTML;
            $mail->Send();
            return true;
        } catch (phpmailerException $e) {
            return false;
        }
    } 

    //Función para enviar correos html con la clase phpmailer v. 5, usando smtp.
    //$to puede ser un array q contenga varios destinatarios, o una cadena con una sola dir. de correo
    public function correoHtSmtp($to, $replyTo, $fromName, $subject, $msgHTML, $smtpUsr, $smtpPass, $host='localhost', $port='') {

        require_once 'phpmailer/class.phpmailer.php';
        try {
            $mail = new PHPMailer(true);

            if (is_array($to)) {
                foreach ($to as $destino) {
                    $mail->AddAddress($destino);
                }
            } else { //si es una cadena (mimail@srv.com)
                $mail->AddAddress($to);
            }

            $mail->AddReplyTo($replyTo, $fromName);
            $mail->IsSMTP();  // telling the class to use SMTP
            $mail->SMTPAuth = true; // turn on SMTP authentication
            $mail->IsHTML(true);
            $mail->CharSet = 'UTF-8';
            $mail->Username = $smtpUsr;
            $mail->Password = $smtpPass;
            $mail->Host = $host;
            $mail->Port = $port;
            $mail->From = $smtpUsr;
            $mail->FromName = $fromName;
            $mail->Subject = $subject;
            $mail->Body = $msgHTML;
            $mail->Send();
            return true;
        } catch (phpmailerException $e) {
            //echo $e->errorMessage();
            return false;
        }
    } */
    


    //Valida que los elementos del array muestras que se le envien sean numéricos,
    //$excepciones es un array de cadenas que sí pueden estar en los elementos
    //a validar.	//Los parámetros a validar deben ir después de $excepciones.
    /* protected function validaNumerico($muestra, $excepciones=null, $msgError="Por favor ingresar solo numeros en el campo Telefono.") {
      if (is_array($muestra)) {
      foreach ($muestra as $campo) {
      if (!validaNumerico($campo))
      return $msgError;
      }
      return true;
      }
      else { //ya tenemos una cadena a validar
      if (is_array($excepciones)) {
      foreach ($excepciones as $excepcion) {
      $muestra = str_replace($excepcion, '', $muestra); //quitamos las excepciones de la cadena
      }
      } else {
      $muestra = str_replace($excepciones, '', $muestra); //quitamos las excepciones de la cadena
      }

      if (is_numeric($muestra)) {
      return true;
      } else {
      return $msgError;
      }
      }
      }

     */
}
?>
